/* 
 * created by senllang 2024/12/4 
 * mail : study@senllang.onaliyun.com 
 *
 * Copyright (c) 2023-2024 senllang

 * This is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * 
 */
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <err.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


static void
display_pthread_attr(pthread_attr_t *attr, char *prefix)
{
    int s, i;
    size_t v;
    void *stkaddr;
    struct sched_param sp;

    s = pthread_attr_getdetachstate(attr, &i);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sDetach state        = %s\n", prefix,
           (i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" : (i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE"
                                                                                                       : "???");

    s = pthread_attr_getscope(attr, &i);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sScope               = %s\n", prefix,
           (i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" : (i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS"
                                                                                               : "???");

    s = pthread_attr_getinheritsched(attr, &i);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sInherit scheduler   = %s\n", prefix,
           (i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" : (i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED"
                                                                                                  : "???");

    s = pthread_attr_getschedpolicy(attr, &i);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sScheduling policy   = %s\n", prefix,
           (i == SCHED_OTHER) ? "SCHED_OTHER" : (i == SCHED_FIFO) ? "SCHED_FIFO"
                                            : (i == SCHED_RR)     ? "SCHED_RR"
                                                                  : "???");

    s = pthread_attr_getschedparam(attr, &sp);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sScheduling priority = %d\n", prefix, sp.sched_priority);

    s = pthread_attr_getguardsize(attr, &v);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sGuard size          = %zu bytes\n", prefix, v);

    s = pthread_attr_getstack(attr, &stkaddr, &v);
    if (s != 0)
        exit(EXIT_FAILURE);
    printf("%sStack address       = %p\n", prefix, stkaddr);
    printf("%sStack size          = %#zx bytes\n", prefix, v);
}



static void *
thread_start(void *arg)
{
    int s;
    pthread_attr_t gattr;
    pthread_t threadid; 
    int i = 0;

    /* pthread_getattr_np() is a non-standard GNU extension that
       retrieves the attributes of the thread specified in its
       first argument. */
    threadid = pthread_self();

    s = pthread_getattr_np(threadid, &gattr);
    if (s != 0)
        exit(EXIT_FAILURE);

    for(i = 0; i < 1000000; i++)
    {
        printf("thread %lu cycle %d\n", threadid, i);
    }


    sleep(5);
    printf("Thread attributes:\n");
    display_pthread_attr(&gattr, "\t");

    exit(EXIT_SUCCESS); /* Terminate all threads */
}

void InitAttrPolicy(pthread_attr_t *attr, int po, int pri)
{
    int policy = po;
    struct sched_param sp;

    sp.sched_priority = pri; 
    pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setschedpolicy(attr, policy);
    pthread_attr_setschedparam(attr, &sp);
}

void InitAttrScope(pthread_attr_t *attr, int scope)
{
    pthread_attr_setscope(attr, scope);
}

int main(int argc, char *argv[])
{
    pthread_t thr;
    pthread_attr_t attr;
    pthread_attr_t *attrp; /* NULL or &attr */
    int s;

    attrp = NULL;

    /* If a command-line argument was supplied, use it to set the
       stack-size attribute and set a few other thread attributes,
       and set attrp pointing to thread attributes object. */

    if (argc > 1)
    {
        size_t stack_size;
        void *sp;

        attrp = &attr;

        s = pthread_attr_init(&attr);
        if (s != 0)
            exit(EXIT_FAILURE);

        s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if (s != 0)
            exit(EXIT_FAILURE);

        s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
        if (s != 0)
            exit(EXIT_FAILURE);

        stack_size = strtoul(argv[1], NULL, 0);

        s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size);
        if (s != 0)
            exit(EXIT_FAILURE);

        printf("posix_memalign() allocated at %p\n", sp);

        s = pthread_attr_setstack(&attr, sp, stack_size);
        if (s != 0)
            exit(EXIT_FAILURE);
    }

    //InitAttrPolicy(&attr, SCHED_OTHER, 80);
    InitAttrScope(&attr, PTHREAD_SCOPE_PROCESS);
    s = pthread_create(&thr, &attr, &thread_start, NULL);
    s = pthread_create(&thr, &attr, &thread_start, NULL);
    if (s != 0)
        exit(EXIT_FAILURE);

    if (attrp != NULL)
    {
        s = pthread_attr_destroy(attrp);
        if (s != 0)
            exit(EXIT_FAILURE);
    }

    pause(); /* Terminates when other thread calls exit() */
}